using System;
using System.Collections;
using Framework.Save;
using System.Collections.Generic;
using LitJson;

namespace GameCore
{
    public abstract class GameData : IJsonSerializable
    {
        public GameData() { }
        public GameData(JsonData json) => Deserialize(json);
        public abstract JsonData Serialize();
        public abstract void Deserialize(JsonData data);

        private static readonly Dictionary<string, Type> _typeMap = new();
        static GameData()
        {
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in asm.GetTypes())
                {
                    if (!type.IsAbstract && typeof(GameData).IsAssignableFrom(type))
                    {
                        _typeMap[type.Name] = type;
                    }
                }
            }
        }

        private static GameData DeserializeObject(JsonData json)
        {
            if (!json.ContainsKey("type")) return new ObjectData(json);
            var key = (string)json["type"];
            if (!_typeMap.TryGetValue(key, out var type)) return new ObjectData(json);
            var ret = (GameData)(Activator.CreateInstance(type));
            ret.Deserialize(json);
            return ret;
        }

        public static implicit operator GameData(JsonData json)
        {
            return json.GetJsonType() switch
            {
                JsonType.Boolean => new BoolData((bool)json),
                JsonType.Int => new IntData((int)json),
                JsonType.Long => new IntData((int)json),
                JsonType.Double => new FloatData((float)json),
                JsonType.String => new StringData((string)json),
                JsonType.Array => new ListData(json),
                JsonType.Object => DeserializeObject(json),
                _ => null
            };
        }

        public static implicit operator JsonData(GameData data) => data.Serialize();
        
        public static implicit operator GameData(bool value) => new BoolData(value);
        public static implicit operator GameData(int value) => new IntData(value);
        public static implicit operator GameData(long value) => new IntData((int)value);
        public static implicit operator GameData(float value) => new FloatData(value);
        public static implicit operator GameData(double value) => new FloatData((float)value);
        public static implicit operator GameData(string value) => new StringData(value);

        public static implicit operator bool(GameData data) => data is BoolData b ? b.Value : false;
        public static implicit operator int(GameData data) => data is IntData i ? i.Value : 0;
        public static implicit operator long(GameData data) => data is IntData i ? i.Value : 0;
        public static implicit operator float(GameData data) => data is FloatData f ? f.Value : 0f;
        public static implicit operator double(GameData data) => data is FloatData f ? f.Value : 0d;
        public static implicit operator string(GameData data) => data is StringData s ? s.Value : null;
    }

    public class BoolData : GameData
    {
        public bool Value;
        public BoolData(bool value) : base() => Value = value;
        public BoolData(JsonData json) : base(json) { }
        public override JsonData Serialize() => Value;
        public override void Deserialize(JsonData data) => Value = (bool)data;
    }

    public class IntData : GameData
    {
        public int Value;
        public IntData(int value) => Value = value;
        public override JsonData Serialize() => Value;
        public override void Deserialize(JsonData data) => Value = (int)data;
    }

    public class FloatData : GameData
    {
        public float Value;
        public FloatData(float value) => Value = value;
        public override JsonData Serialize() => Value;
        public override void Deserialize(JsonData data) => Value = (float)data;
    }

    public class StringData : GameData
    {
        public string Value;
        public StringData(string value) => Value = value;
        public override JsonData Serialize() => Value;
        public override void Deserialize(JsonData data) => Value = (string)data;
    }

    public class ListData : GameData, IList<GameData>
    {
        private readonly List<GameData> data = new();
        public ListData() { }
        internal ListData(JsonData json) => Deserialize(json);
        
        public override JsonData Serialize()
        {
            var json = JsonUtil.NewArray();
            foreach (GameData data in data)
                json.Add(data.Serialize());
            return json;
        }

        public override void Deserialize(JsonData json)
        {
            data.Clear();
            foreach (JsonData item in json)
                data.Add(item);
        }
        
        #region List
        
        public GameData this[int index]
        {
            get => data[index];
            set => data[index] = value;
        }
        
        public int Count => data.Count;
        public bool IsReadOnly => false;
        
        public void Add(GameData item) => data.Add(item);
        public void Insert(int index, GameData item) => data.Insert(index, item);
        public bool Remove(GameData item) => data.Remove(item);
        public void RemoveAt(int index) => data.RemoveAt(index);
        public void Clear() => data.Clear();
        
        public bool Contains(GameData item) => data.Contains(item);
        public int IndexOf(GameData item) => data.IndexOf(item);
        public void CopyTo(GameData[] array, int arrayIndex) => data.CopyTo(array, arrayIndex);
        public IEnumerator<GameData> GetEnumerator() => data.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => data.GetEnumerator();

        #endregion
    }
    
    public class ObjectData : GameData, IDictionary<string, GameData>
    {
        private readonly Dictionary<string, GameData> data = new();
        public ObjectData() { }
        public ObjectData(JsonData json) => Deserialize(json);
        
        public override JsonData Serialize()
        {
            var json = JsonUtil.NewObject<ObjectData>();
            foreach (var (key, value) in data)
                json[key] = value.Serialize();
            return json;
        }

        public override void Deserialize(JsonData json)
        {
            data.Clear();
            foreach (KeyValuePair<string, JsonData>item in json)
                data.Add(item.Key, item.Value);
        }
        
        #region Dictionary
        /* ===================== 常用字典接口 ===================== */
        public GameData this[string key]
        {
            get => data[key];
            set => data[key] = value;
        }
        
        public int Count => data.Count;
        public bool IsReadOnly => false;
        public ICollection<string> Keys => data.Keys;
        public ICollection<GameData> Values => data.Values;

        public void Add(string key, GameData value) => data.Add(key, value);
        public bool Remove(string key) => data.Remove(key);
        public bool ContainsKey(string key) => data.ContainsKey(key);
        public bool ContainsValue(GameData value) => data.ContainsValue(value);
        public bool TryGetValue(string key, out GameData value) => data.TryGetValue(key, out value);
        public void Clear() => data.Clear();
        
        public IEnumerator<KeyValuePair<string, GameData>> GetEnumerator() => data.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => data.GetEnumerator();
        /* ===================== 显式接口实现 ===================== */
        public void Add(KeyValuePair<string, GameData> item) => data.Add(item.Key, item.Value);
        public bool Remove(KeyValuePair<string, GameData> item) => data.Remove(item.Key);
        public bool Contains(KeyValuePair<string, GameData> item) => (data as IDictionary).Contains(item);
        public void CopyTo(KeyValuePair<string, GameData>[] array, int arrayIndex) => (data as IDictionary).CopyTo(array, arrayIndex);

        #endregion
    }

    public abstract class ObjectDataWrapper<T> : GameData where T : ObjectDataWrapper<T>
    {
        public ObjectData data = new();
        public void SetData(string key, GameData value) => data[key] = value;
        public bool HasData(string key) => data.ContainsKey(key);
        public GameData GetData(string key) => data[key];

        public override JsonData Serialize()
        {
            var json = JsonUtil.NewObject<T>();
            json["data"] = data.Serialize();
            return json;
        }
        public override void Deserialize(JsonData json) 
            => data = !json.ContainsKey("data") ? new ObjectData() : new ObjectData(json["data"]);
    }
}